package com.jzo2o.health.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.expcetions.BadRequestException;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.expcetions.DBException;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.model.PageResult;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.CollUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.health.enums.OrderPayStatusEnum;
import com.jzo2o.health.enums.OrderStatusEnum;
import com.jzo2o.health.handler.OrdersHandler;
import com.jzo2o.health.mapper.OrdersMapper;
import com.jzo2o.health.mapper.ReservationSettingMapper;
import com.jzo2o.health.model.UserThreadLocal;
import com.jzo2o.health.model.domain.*;
import com.jzo2o.health.model.dto.request.OrdersCancelReqDTO;
import com.jzo2o.health.model.dto.request.OrdersPageQueryReqDTO;
import com.jzo2o.health.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.health.model.dto.response.*;
import com.jzo2o.health.service.*;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.mysql.utils.PageUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * @Description OrdersServiceImpl
 * @Author kkk
 * @Date 2024-12-14
 */
@Slf4j
@Service
public class OrdersServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersService {

    @Autowired
    private ISetmealService setmealService;

    @Autowired
    private IReservationSettingService reservationSettingService;

    @Autowired
    private IMemberService memberService;

    @Autowired
    private ReservationSettingMapper reservationSettingMapper;

    @Autowired
    private TradingApi tradingApi;

    @Autowired
    private IOrdersService owner;

    @Autowired
    private IOrdersCancelledService ordersCancelledService;

    @Autowired
    private OrdersHandler ordersHandler;

    @Autowired
    private IOrdersRefundService ordersRefundService;

    private List<Integer> cancelStatusList = Arrays.asList(OrderStatusEnum.NO_PAY.getStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus());

    @Override
    public PlaceOrderResDTO place(PlaceOrderReqDTO dto) {
        //套餐id
        Setmeal setmeal = setmealService.getById(dto.getSetmealId());
        if (ObjectUtil.isNull(setmeal)){
            throw new ForbiddenOperationException("套餐不存在");
        }

        //预约日期
        ReservationSetting rs = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .apply("number>reservations")
                .one();
        if (ObjectUtil.isNull(rs)){
            throw new ForbiddenOperationException("已约满");
        }

        //插入数据库
        Orders orders = BeanUtil.toBean(dto, Orders.class);
        orders.setOrderStatus(OrderStatusEnum.NO_PAY.getStatus());
        orders.setPayStatus(OrderPayStatusEnum.NO_PAY.getStatus());

        orders.setSetmealName(setmeal.getName());
        orders.setSetmealSex(setmeal.getSex());
        orders.setSetmealAge(setmeal.getAge());
        orders.setSetmealImg(setmeal.getImg());
        orders.setSetmealRemark(setmeal.getRemark());
        orders.setSetmealPrice(BigDecimal.valueOf(setmeal.getPrice()));

        Long userId= UserThreadLocal.currentUserId();

        Member member = memberService.getById(userId);

        orders.setMemberId(userId);
        orders.setMemberPhone(member.getPhone());

        orders.setSortBy(System.currentTimeMillis());

        this.save(orders);

        ReservationSetting reservationSetting = reservationSettingService.lambdaQuery()
                .eq(ReservationSetting::getOrderDate, dto.getReservationDate())
                .one();
        if (ObjectUtil.isNull(reservationSetting)){
            throw new ForbiddenOperationException("无法预约这一天");
        }

        Integer row = reservationSettingMapper.updateReservations(reservationSetting.getId());
        if (row==0){
            throw new DBException("无法预约这一天");
        }

        return new PlaceOrderResDTO(orders.getId());
    }


    //订单二维码生成后更新订单表数据
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateOrder(Long id, NativePayResDTO nativePayResDTO) {
        //判断订单是否存在
        Orders orders = this.lambdaQuery()
                .eq(Orders::getId, id)
                .one();
        if (ObjectUtils.isNull(orders)){
            throw new BadRequestException("订单不存在");
        }

        //更新订单
        boolean result = this.lambdaUpdate()
                .set(Orders::getTradingChannel, nativePayResDTO.getTradingChannel())
                .set(Orders::getTradingOrderNo, nativePayResDTO.getTradingOrderNo())
                .update();
        if (!result){
            throw new DBException("订单更新失败");
        }

    }

    //查询订单支付结果
    @Override
    public OrdersPayResDTO payResult(Long id) {
        //判断订单是否存在
        Orders orders = this.getById(id);
        if (ObjectUtils.isNull(orders)){
            throw new BadRequestException("订单不存在");
        }

        //判断支付服务订单号是否存在
        if (ObjectUtils.isEmpty(orders.getTradingOrderNo())){
            throw new BadRequestException("订单不存在");
        }

        //判断订单状态是否支付成功
        OrdersPayResDTO ordersPayResDTO = BeanUtils.copyBean(orders, OrdersPayResDTO.class);
        ordersPayResDTO.setProductOrderNo(id);
        if (ObjectUtils.equal(orders.getPayStatus(),OrderPayStatusEnum.PAY_SUCCESS.getStatus())){//订单未支付成功状态
            return ordersPayResDTO;
        }

        //调用feign接口查询订单状态
        TradingResDTO tradingResDTO = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());

        //更新订单表信息(支付时间、第三方交易号、订单状态、支付状态)
        if (ObjectUtils.isNotEmpty(tradingResDTO)&&ObjectUtils.equal(tradingResDTO.getTradingState(), TradingStateEnum.YJS)){
            //更新订单
            owner.paySuccess(tradingResDTO.getProductOrderNo(),tradingResDTO.getTransactionId(),tradingResDTO.getPaySuccessTime());
            ordersPayResDTO.setQrCode(tradingResDTO.getQrCode());//二维码base64数据
            ordersPayResDTO.setPayStatus(OrderPayStatusEnum.PAY_SUCCESS.getStatus());//订单状态
        }

        //6. 封装响应结果并返回
        return ordersPayResDTO;
    }

    //订单支付成功，更新订单状态
    @Transactional(rollbackFor = Exception.class)
    public void paySuccess(Long orderId, String transactionId, LocalDateTime paySuccessTime) {
        boolean result = this.lambdaUpdate()
                .eq(Orders::getId, orderId)//订单id
                .set(Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())//支付状态
                .set(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus())//订单状态
                //.set(Orders::getTransactionId, tradingResDTO.getTransactionId())//第三方支付的支付ID
                .set(Orders::getPayTime,paySuccessTime)//支付时间
                .set(Orders::getTransactionId,transactionId)//第三方支付的支付id
                .update();
        if (!result){
            throw new DBException("更新订单失败");
        }
    }

    @Override
    public void cancel(OrdersCancelReqDTO ordersCancelReqDTO) {
        //1.判断订单是否存在
        Orders orders = this.getById(ordersCancelReqDTO.getId());
        if (orders == null) {
            throw new BadRequestException("订单不存在");
        }

        //2.判断订单状态是否合法（待支付、待体检）
        if (!cancelStatusList.contains(orders.getOrderStatus())) {
            throw new BadRequestException("非法订单");
        }

        //3.如果是待支付订单（则添加取消记录、更新订单状态）
        if (ObjectUtils.equals(orders.getOrderStatus(), OrderStatusEnum.NO_PAY.getStatus())) {
            owner.cancelOrderByNoPay(orders.getId(), ordersCancelReqDTO.getCancelReason(), false);
        } else {
            //4.如果是待派单订单（则添加取消记录、更新订单状态、发起退款申请及后续处理）
        }
    }

    /**
     * 取消未支付订单
     *
     * @param orderId      订单ID
     * @param cancelReason 取消原因
     * @param isSystem     是否系统自动触发 true-系统触发 false-人为触发
     */
    @Override
    public void cancelOrderByNoPay(Long orderId, String cancelReason, boolean isSystem) {
        //1. 添加取消记录
        OrdersCancelled ordersCanceled = new OrdersCancelled();
        ordersCanceled.setId(orderId);//订单id
        if (!isSystem) {
            ordersCanceled.setCancellerId(UserContext.currentUserId());//取消人id
            ordersCanceled.setCancellerType(UserContext.currentUser().getUserType());//取消人类型
            ordersCanceled.setCancellerName(UserContext.currentUser().getName());//取消人姓名
        }
        ordersCanceled.setCancelReason(cancelReason);//取消原因
        ordersCanceled.setCancelTime(LocalDateTime.now());//取消时间
        boolean resultForCancel = ordersCancelledService.save(ordersCanceled);
        if (!resultForCancel) {
            throw new DBException("添加取消记录失败");
        }

        //2. 更新订单状态
        boolean resultForOrders = this.lambdaUpdate()
                .eq(Orders::getId, orderId)
                .set(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus())
                .update();
        if (!resultForOrders) {
            throw new DBException("更新订单失败");
        }
    }

    //订单退款
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refund(OrdersCancelReqDTO ordersCancelReqDTO) {
        // 更新三个表
        // 1.判断是否已支付 且 待体检 状态 更新订单表
        Orders orders = baseMapper.selectById(ordersCancelReqDTO.getId());
        if (ObjectUtils.isNull(orders)) {
            throw new CommonException("订单不存在");
        }
        if (ObjectUtils.equals(orders.getOrderStatus(), OrderStatusEnum.WAITING_CHECKUP.getStatus())
                && ObjectUtils.equals(orders.getPayStatus(), OrderPayStatusEnum.PAY_SUCCESS.getStatus())) {
            // 订单状态改为已关闭
            orders.setOrderStatus(OrderStatusEnum.CLOSED.getStatus());
            // 支付状态改为退款中
            orders.setPayStatus(OrderPayStatusEnum.REFUNDING.getStatus());
            boolean updated = updateById(orders);
            if (!updated) {
                throw new CommonException("订单退款失败");
            }
            // 2.更新退款记录表（远程调用支付服务退款的标记）
            updateOrdersRefund(orders);
            // 3.更新取消表
            updateOrderCancelled(orders, ordersCancelReqDTO);
            // 4.启动一个线程及时退款
            ordersHandler.requestRefundNewThread(orders.getId());
        }
    }

    /**
     * 更新订单状态为已取消
     *
     * @param orders 订单信息
     * @param ordersCancelReqDTO 订单取消请求数据传输对象
     *
     * 该方法首先恢复与订单相关的预留资源，然后将订单取消信息插入到订单取消表中
     * 如果订单取消失败，将抛出异常
     */
    @Override
    public void updateOrderCancelled(Orders orders, OrdersCancelReqDTO ordersCancelReqDTO) {
        // 恢复名额
        reservationSettingService.recoverReservation(orders.getReservationDate());
        // 插入订单取消表
        // 获取用户信息
        Long memberId = orders.getMemberId();
        Member member = memberService.getById(memberId);
        String nickname = member.getNickname();
        OrdersCancelled ordersCancelled = new OrdersCancelled();
        ordersCancelled.setId(orders.getId());
        ordersCancelled.setCancellerId(memberId);
        ordersCancelled.setCancellerName(nickname);
        ordersCancelled.setCancellerType(1);
        ordersCancelled.setCancelReason(ordersCancelReqDTO.getCancelReason());
        ordersCancelled.setCancelTime(LocalDateTime.now());
        boolean save = ordersCancelledService.save(ordersCancelled);
        if (!save) {
            throw new CommonException("订单取消失败");
        }
    }

    /**
     * 更新订单退款信息
     * 当订单发生退款时，此方法用于创建并保存订单退款记录
     *
     * @param orders 订单对象，包含需要更新退款信息的订单数据
     * @throws CommonException 如果退款记录保存失败，抛出此异常
     */
    @Override
    public void updateOrdersRefund(Orders orders) {
        // 创建一个新的订单退款对象
        OrdersRefund ordersRefund = new OrdersRefund();
        // 设置退款记录的ID为订单的ID
        ordersRefund.setId(orders.getId());
        // 设置退款记录的交易订单号为订单的交易订单号
        ordersRefund.setTradingOrderNo(orders.getTradingOrderNo());
        // 设置退款记录的实际支付金额为订单的套餐价格
        ordersRefund.setRealPayAmount(orders.getSetmealPrice());
        // 尝试保存订单退款记录
        boolean save = ordersRefundService.save(ordersRefund);
        // 如果保存失败，抛出异常
        if (!save) {
            throw new CommonException("退款记录表更新失败");
        }
    }

    //滚动分页查询
    @Override
    public List<OrdersResDTO> pageQueryForUser(Integer ordersStatus, Long sortBy) {

        // 根据当前用户id查询所属的所有订单
        Long userId = UserThreadLocal.currentUserId();
        // 每次进行翻页时sortBy表示最后一个订单字段值
        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<Orders>()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrderStatus, ordersStatus)
                .eq(Orders::getMemberId, userId);

        // 初始化分页对象
        Page<Orders> queryPage = new Page<>();
        // 添加排序规则，按sort_by字段降序排列
        queryPage.addOrder(OrderItem.desc("sort_by"));

        // 查询订单
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        // 获取查询结果列表
        List<Orders> records = ordersPage.getRecords();
        // 将查询结果转换为DTO列表，便于前端展示
        List<OrdersResDTO> ordersResDTOS = BeanUtils.copyToList(records, OrdersResDTO.class);
        return ordersResDTOS;
    }

    //根据订单id查询
    @Override
    public OrdersDetailResDTO getByIdForUser(Long id) {

        // 根据ID获取订单信息
        Orders orders = this.getById(id);
        // 检查订单是否存在
        if (ObjectUtils.isNull(orders)) {
            // 如果订单不存在，抛出异常
            throw new BadRequestException("当前订单不存在");
        }

        // 将订单信息转换为OrdersDetailResDTO对象
        OrdersDetailResDTO result = BeanUtils.toBean(orders, OrdersDetailResDTO.class);

        // 根据ID获取订单取消信息
        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);
        // 检查订单取消信息是否存在
        if (ObjectUtils.isNotNull(ordersCancelled)) {
            // 如果存在，将取消原因和取消时间添加到结果中
            result.setCancelReason(ordersCancelled.getCancelReason());
            result.setCancelTime(ordersCancelled.getCancelTime());
        }

        // 返回订单详情DTO对象
        return result;
    }

    //管理端分页查询
    @Override
    public PageResult<OrdersResDTO> pageQueryForAdmin(OrdersPageQueryReqDTO ordersPageQueryReqDTO) {

        Page<Orders> ordersPage = PageUtils.parsePageQuery(ordersPageQueryReqDTO, Orders.class);

        LambdaQueryWrapper<Orders> queryWrapper = new LambdaQueryWrapper<Orders>()
                .in(CollUtils.isNotEmpty(ordersPageQueryReqDTO.getIds()), Orders::getId, ordersPageQueryReqDTO.getIds())
                .eq(ObjectUtils.isNotNull(ordersPageQueryReqDTO.getOrderStatus()), Orders::getOrderStatus, ordersPageQueryReqDTO.getOrderStatus())
                .eq(StringUtils.isNotBlank(ordersPageQueryReqDTO.getMemberPhone()), Orders::getMemberPhone, ordersPageQueryReqDTO.getMemberPhone());

        Page<Orders> selectPage = baseMapper.selectPage(ordersPage, queryWrapper);
        return PageUtils.toPage(selectPage, OrdersResDTO.class);
    }

    @Override
    public OrdersCountResDTO countByStatus() {

        Integer noPayCount = this.lambdaQuery().eq(Orders::getOrderStatus, OrderStatusEnum.NO_PAY.getStatus()).count();
        Integer waitingCheckupCount = this.lambdaQuery().eq(Orders::getOrderStatus, OrderStatusEnum.WAITING_CHECKUP.getStatus()).count();
        Integer completedCheckupCount = this.lambdaQuery().eq(Orders::getOrderStatus, OrderStatusEnum.COMPLETED_CHECKUP.getStatus()).count();
        Integer closedCount = this.lambdaQuery().eq(Orders::getOrderStatus, OrderStatusEnum.CLOSED.getStatus()).count();
        Integer cancelledCount = this.lambdaQuery().eq(Orders::getOrderStatus, OrderStatusEnum.CANCELLED.getStatus()).count();
        Integer totalCount = this.lambdaQuery().count();

        OrdersCountResDTO result = new OrdersCountResDTO(noPayCount, waitingCheckupCount, completedCheckupCount, closedCount, cancelledCount, totalCount);

        return result;
    }

    //管理端根据订单id查询
    @Override
    public AdminOrdersDetailResDTO getByIdForAdmin(Long id) {

        Orders orders = this.lambdaQuery()
                .eq(Orders::getId, id)
                .one();
        if(ObjectUtils.isNull(orders)){
            throw new BadRequestException("当前订单不存在");
        }

        AdminOrdersDetailResDTO adminOrdersDetailResDTO = new AdminOrdersDetailResDTO();

        AdminOrdersDetailResDTO.OrderInfo orderInfo = BeanUtils.toBean(orders, AdminOrdersDetailResDTO.OrderInfo.class);
        AdminOrdersDetailResDTO.PayInfo payInfo = BeanUtils.toBean(orders, AdminOrdersDetailResDTO.PayInfo.class);
        payInfo.setThirdOrderId(orders.getTransactionId());
        payInfo.setPayTime(orders.getPayTime());
        AdminOrdersDetailResDTO.RefundInfo refundInfo = BeanUtils.toBean(orders, AdminOrdersDetailResDTO.RefundInfo.class);

        OrdersCancelled ordersCancelled = ordersCancelledService.getById(id);
        if(ObjectUtils.isNotNull(ordersCancelled)){

            AdminOrdersDetailResDTO.CancelInfo cancelInfo = new AdminOrdersDetailResDTO.CancelInfo();
            cancelInfo.setCancelReason(ordersCancelled.getCancelReason());
            cancelInfo.setCancelTime(ordersCancelled.getCancelTime());

            adminOrdersDetailResDTO.setCancelInfo(cancelInfo);
        }

        adminOrdersDetailResDTO.setOrderInfo(orderInfo);
        adminOrdersDetailResDTO.setPayInfo(payInfo);
        adminOrdersDetailResDTO.setRefundInfo(refundInfo);

        return adminOrdersDetailResDTO;
    }
}
